﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Diagnostics;
using System.Xml;
using Pharmacy.Ultilities.Configurations;


namespace Pharmacy.Ultilities.Localization
{
    /// <summary>
    /// Provides methods to support for Multi-Language feature.
    /// </summary>
    /// <author>nghiemhoang</author>
    public static class LocalizerText
    {
        /// <summary>
        /// Gets or sets the dictionary that contains key and value of displaying language.
        /// </summary>
        private static Dictionary<string, string> _dictionary;
        
        /// <summary>
        /// Indicates the localizated text associated given key exists in the dictionary or not.
        /// </summary>
        /// <param name="key">Key of localizated text.</param>
        /// <author>ThangTran</author>
        /// <history>
        /// 2011.03.21 - ThangTran: refactored code.
        /// 2011.04.06 - ThangTran: added code to check the dictionary is intialized.
        /// </history>
        public static bool CanGetText(string key)
        {
            // --- Begin parameters checking code -----------------------------
            Debug.Assert(!string.IsNullOrEmpty(key));
            // --- End parameters checking code -------------------------------

            return _dictionary != null && _dictionary.ContainsKey(key);
        }

        /// <summary>
        /// Gets the localizated text by given key.
        /// If the key is not found, the default text will be returned.
        /// If default text is null, a special message will be shown to indicate the text is not localized.
        /// </summary>
        /// <param name="key">Key of localizated text.</param>
        /// <param name="defaultText">Default text will be used when the text is not found. This is an optional parameter.</param>
        /// <param name="args">Arguments will be used to format the returned text.</param>
        /// <author>ThangTran</author>
        /// <history>
        /// </history>
        public static string GetText(string key, string defaultText, params object[] args)
        {
            // --- Begin parameters checking code -----------------------------
            Debug.Assert(!string.IsNullOrEmpty(key));
            // --- End parameters checking code -------------------------------

            // check if the dictionary is initialized
            string fullKeyName = CombineKeys(key);
            string value;
            Dictionary<string, string> dictionary = LocalizerText.DictionaryLocalize;
            if (dictionary != null)
            {
                // current text is not inserted to language file
#if DEBUG
                //defaultText != null && defaultText != string.Empty && //minhhungle@gmail.com - 2012.08.01: add missing key to language file
                //even if the defaultText is null.
                if (LocalizerText.CanGetText(fullKeyName) == false)
                {
                    LocalizerText.WriteTextToFile(fullKeyName, defaultText);
                }
#endif

                if (dictionary.TryGetValue(fullKeyName, out value))
                {
                    // found                
                    return args.Length == 0 ? value : string.Format(value, args);
                }
            }



            // not found or the dictionary is not yet initialized
            return defaultText != null
                       ? (args.Length == 0 ? defaultText : string.Format(defaultText, args))
                       : string.Format("$[{0}] not found.", key);
        }        

        /// <summary>
        /// Gets the localizated text by given key within specified view.
        /// If the key is not found, the default text will be returned.
        /// If default text is null, a special message will be shown to indicate the text is not localized.
        /// </summary>
        /// <param name="view">Reference to the view. This is an optional parameter.</param>
        /// <param name="key">Key of localizated text.</param>
        /// <param name="defaultText">Default text will be used when the text is not found. This is an optional parameter.</param>
        /// <param name="args">Arguments will be used to format the returned text.</param>
        /// <author>ThangTran</author>
        /// <history>
        /// 2011.03.21 - ThangTran: refactored code.
        /// </history>
        
        /// <summary>
        /// Combines given key fragments into one, removing null and empty keys.
        /// </summary>
        /// <param name="keys">Key fragments.</param>
        /// <returns></returns>
        /// <author>ThangTran</author>
        /// <history>
        /// 2011.03.02 - ThangTran: added.
        /// 2011.03.21 - ThangTran: refactored code.
        /// </history>
        public static string CombineKeys(params string[] keys)
        {
            // --- Begin parameters checking code -----------------------------
            Debug.Assert(keys != null && keys.Length >= 1);
            // --- End parameters checking code -------------------------------

            // filter down null & empty keys
            return string.Join(".",
                               LinqToObject
                                   .From(keys)
                                   .Where(delegate(string t) { return !string.IsNullOrEmpty(t); })
                                   .ToArray());
        }

        /// <summary>
        /// Initializes multi-language mode.
        /// </summary>
        /// <author>ThangTran</author>
        /// <history>
        /// 2011.08.23 - ThangTran: added.
        /// </history>
        public static void InitializeMultiLanguage()
        {
            // get language file path
            string languageFile = IOHelper.GetFullDirectoryName(string.Format("Languages\\{0}.xml", ConfigManager.DIOConfig[Constant.CURRENTLANGUAGE]));//ConfigManager.DIOConfig.Language

            // initialize dictionary
            _dictionary = new Dictionary<string, string>();

            if (!File.Exists(languageFile))
            {
                CreateLanguageFile(languageFile);
            }

            // load into dictionary
            XmlDocument document = new XmlDocument();
            document.Load(languageFile);
            
            // select all nodes
            XmlNodeList xmlItemList = document.SelectNodes("Localization/Item");

            // cancel if not found
            if (xmlItemList == null)
            {
                return;
            }

            // for each node
            foreach (XmlNode xmlNode in xmlItemList)
            {
                // read key & value
                string key = XmlHelper.Select(xmlNode, "@Key");
                string value = XmlHelper.Select(xmlNode, "@Value");

                // make sure they are not null
                Debug.Assert(key != null);                

                // check if the dictionary already contains the key
                if (_dictionary.ContainsKey(key))
                {
                    // ignore it
                    continue;
                }

                // add to the dictionary
                _dictionary.Add(key, value ?? string.Empty);
            }
        }

        /// <summary>
        /// Write current content to current language file
        /// </summary>
        /// <param name="key"></param>
        /// <param name="value"></param>
        public static void WriteTextToFile(string key, string value)
        {
            // get language file path                        
            string languageFile = IOHelper.GetFullDirectoryName(string.Format("Languages\\{0}.xml", ConfigManager.DIOConfig[Constant.CURRENTLANGUAGE]));//ConfigManager.DIOConfig.Language                        
            // load into dictionary
            XmlDocument document = new XmlDocument();
            try
            {
                document.Load(languageFile);
            }
            catch (XmlException xmlException) // Trường hợp file tồn tại, nhưng nội dung bên trong đã bị xóa hết.
            {
                if (xmlException.Message.Contains("Root element is missing"))
                {
                    CreateLanguageFile(languageFile);
                    document.Load(languageFile);
                }
            }
            XmlElement eleRoot = document.DocumentElement;
            // create "Localization" element if it's not existed
            XmlNode rootNode = eleRoot.SelectSingleNode("//Localization");
            if (rootNode == null)
            {
                XmlElement root = document.CreateElement("Localization", "http://www.w3.org/2001/XMLSchema-instance");                
                document.AppendChild(root);
                rootNode = root.FirstChild;
            }

            // create "Item" element for current missing node
            XmlElement eleItem = document.CreateElement("Item");
            XmlAttribute attributeKey = document.CreateAttribute("Key");
            attributeKey.Value = key;
            XmlAttribute attributeValue = document.CreateAttribute("Value");
            attributeValue.Value = value;
            eleItem.Attributes.Append(attributeKey);
            eleItem.Attributes.Append(attributeValue);;
            rootNode.AppendChild(eleItem);
            document.Save(languageFile);
            // Khởi tạo lại danh sách localize
            InitializeMultiLanguage();
        }

        /// <summary>
        /// Create new file for current language
        /// </summary>
        /// <param name="languageFile"></param>
        public static void CreateLanguageFile(string languageFile)
        {
            // get language file path                        
            IOHelper.PrepareFolderContainsFile(languageFile);
            if (!File.Exists(languageFile))
            {
                FileStream str = File.Create(languageFile);
                str.Close();
            }
            // load into dictionary
            XmlDocument document = new XmlDocument();
            XmlElement eleRoot = document.DocumentElement;
            XmlDeclaration xmlDeclaration = document.CreateXmlDeclaration("1.0", "utf-8", null);
            document.InsertBefore(xmlDeclaration, eleRoot);
            // create "Localization" element if it's not existed
            XmlElement root = document.CreateElement("Localization");
            document.AppendChild(root);
            document.Save(languageFile);            
        }

        /// <summary>
        /// Gets list of available languages.
        /// </summary>
        public static AvailableLanguage[] GetAvailableLanguages()
        {
            return LinqToObject
                .From(Directory.GetFiles(IOHelper.CombinePath(IOHelper.ExecutePath, "Languages")))
                .Select<AvailableLanguage>(delegate(string t) { return new AvailableLanguage(t); })
                .ToArray();
        }

        public static Dictionary<string, string> DictionaryLocalize
        {
            get {
                return _dictionary;
            }
        }

        #region Nested classes
        
        /// <summary>
        /// Represents language information.
        /// </summary>
        public class AvailableLanguage
        {
            /// <summary>
            /// Gets or sets value of Name.
            /// </summary>
            public string Name
            {
                get { return System.IO.Path.GetFileNameWithoutExtension(this.Path); }
            }

            /// <summary>
            /// Back-end field of property Path.
            /// </summary>
            private string _path;

            /// <summary>
            /// Gets or sets value of Path.
            /// </summary>
            public string Path
            {
                get { return _path; }
                set
                {
                    _path = value;
                }
            }
		
            /// <summary>
            /// Initializes a new instance of the <see cref="AvailableLanguage"/> class.
            /// </summary>
            public AvailableLanguage(string path)
            {
                Path = path;
            }

            /// <summary>
            /// Returns a <see cref="T:System.String"/> that represents the current <see cref="T:System.Object"/>.
            /// </summary>
            /// <returns>
            /// A <see cref="T:System.String"/> that represents the current <see cref="T:System.Object"/>.
            /// </returns>
            /// <filterpriority>2</filterpriority>
            public override string ToString()
            {
                return this.Name;
            }
        }

        #endregion        
    }
}
